/*
 *
 *
 *
 *
 * Scaloid: Simpler Android
 *
 * http://scaloid.org
 *
 *
 *
 *
 *
 *
 * Copyright 2013 Sung-Ho Lee and Scaloid contributors
 *
 * Sung-Ho Lee and Scaloid contributors licenses this file to you under the Apache License,
 * version 2.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */

/*
 * This file is automatically generated. Any changes on this file will be OVERWRITTEN!
 * To learn how to contribute, please refer to:
 * https://github.com/pocorall/scaloid/wiki/Inside-Scaloid
 */

package org.scaloid.common

import android.app.Activity
import android.content._
import android.util.Log
import android.os._
import org.scaloid.common.LocalService

import scala.reflect._
import scala.language.experimental.macros

class EventSource0[T] {
  var events: Vector[() => T] = Vector()
  def apply(e: => T): Unit = {
    require(e != null)
    events :+= (() => e)
  }

  def run(): Unit = events.map(_())

  def clear(): Unit = events = Vector()
}

class EventSource1[Arg1, Ret] {
  var events: Vector[Arg1 => Ret] = Vector()
  def apply(e: Arg1 => Ret): Unit = {
    require(e != null)
    events :+= e
  }

  def run(arg: Arg1): Unit = events.map(_(arg))

  def clear(): Unit = events = Vector()
}

class EventSource2[Arg1, Arg2, Ret] {
  var events: Vector[(Arg1, Arg2) => Ret] = Vector()
  def apply(e: (Arg1, Arg2) => Ret): Unit = {
    require(e != null)
    events :+= e
  }

  def run(arg1: Arg1, arg2: Arg2): Unit = events.map(_(arg1, arg2))

  def clear(): Unit = events = Vector()
}

/**
 * Callback handler for classes that can be destroyed.
 */
trait Destroyable {
  protected var onDestroyBodies = Vector[() => Any]()

  def onDestroy(body: => Any) = {
    val el = () => body
    onDestroyBodies :+= el
    el
  }
}

/**
 * Callback handler for classes that can be created.
 */
trait Creatable {
  protected var onCreateBodies = Vector[() => Any]()

  def onCreate(body: => Any) = {
    val el = () => body
    onCreateBodies :+= el
    el
  }
}

/**
 * Callback handler for classes that can be registered and unregistered.
 */
trait Registerable {
  def onRegister(body: => Any): () => Any
  def onUnregister(body: => Any): () => Any
}

/**
 * Automatically generated enriching class of `[[https://developer.android.com/reference/android/content/Context.html android.content.Context]]`.
 */
class RichContext[This <: android.content.Context](val basis: This) extends TraitContext[This]

/**
 * Automatically generated helper trait of `[[https://developer.android.com/reference/android/content/Context.html android.content.Context]]`. This contains several property accessors.
 */
trait TraitContext[This <: android.content.Context] {

  def basis: This

  implicit val ctx = basis

  /**
   * Shortcut for `[[https://developer.android.com/reference/android/content/Context.html#getApplicationContext() getApplicationContext()]]`
   */
  @inline def applicationContext = basis.getApplicationContext

  /**
   * Shortcut for `[[https://developer.android.com/reference/android/content/Context.html#getApplicationInfo() getApplicationInfo()]]`
   */
  @inline def applicationInfo = basis.getApplicationInfo

  /**
   * Shortcut for `[[https://developer.android.com/reference/android/content/Context.html#getAssets() getAssets()]]`
   */
  @inline def assets = basis.getAssets

  /**
   * Shortcut for `[[https://developer.android.com/reference/android/content/Context.html#getCacheDir() getCacheDir()]]`
   */
  @inline def cacheDir = basis.getCacheDir

  /**
   * Shortcut for `[[https://developer.android.com/reference/android/content/Context.html#getClassLoader() getClassLoader()]]`
   */
  @inline def classLoader = basis.getClassLoader

  /**
   * Shortcut for `[[https://developer.android.com/reference/android/content/Context.html#getContentResolver() getContentResolver()]]`
   */
  @inline def contentResolver = basis.getContentResolver

  /**
   * Shortcut for `[[https://developer.android.com/reference/android/content/Context.html#getExternalCacheDir() getExternalCacheDir()]]`
   */
  @inline def externalCacheDir = basis.getExternalCacheDir

  /**
   * Shortcut for `[[https://developer.android.com/reference/android/content/Context.html#getFilesDir() getFilesDir()]]`
   */
  @inline def filesDir = basis.getFilesDir

  /**
   * Shortcut for `[[https://developer.android.com/reference/android/content/Context.html#getMainLooper() getMainLooper()]]`
   */
  @inline def mainLooper = basis.getMainLooper

  /**
   * Shortcut for `[[https://developer.android.com/reference/android/content/Context.html#getObbDir() getObbDir()]]`
   */
  @inline def obbDir = basis.getObbDir

  /**
   * Shortcut for `[[https://developer.android.com/reference/android/content/Context.html#getPackageCodePath() getPackageCodePath()]]`
   */
  @inline def packageCodePath = basis.getPackageCodePath

  /**
   * Shortcut for `[[https://developer.android.com/reference/android/content/Context.html#getPackageManager() getPackageManager()]]`
   */
  @inline def packageManager = basis.getPackageManager

  /**
   * Shortcut for `[[https://developer.android.com/reference/android/content/Context.html#getPackageName() getPackageName()]]`
   */
  @inline def packageName = basis.getPackageName

  /**
   * Shortcut for `[[https://developer.android.com/reference/android/content/Context.html#getPackageResourcePath() getPackageResourcePath()]]`
   */
  @inline def packageResourcePath = basis.getPackageResourcePath

  /**
   * Shortcut for `[[https://developer.android.com/reference/android/content/Context.html#getResources() getResources()]]`
   */
  @inline def resources = basis.getResources

  /**
   * Shortcut for `[[https://developer.android.com/reference/android/content/Context.html#getTheme() getTheme()]]`
   */
  @inline def theme = basis.getTheme

  /**
   * Shortcut for `[[https://developer.android.com/reference/android/content/Context.html#setTheme(int) setTheme(int)]]`
   */
  @inline def theme(p: Int) = theme_=(p)

  /**
   * Shortcut for `[[https://developer.android.com/reference/android/content/Context.html#setTheme(int) setTheme(int)]]`
   */
  @inline def theme_=(p: Int) = { basis.setTheme(p); basis }

  /**
   * Shortcut for `[[https://developer.android.com/reference/android/content/Context.html#getWallpaper() getWallpaper()]]`
   */
  @deprecated("", "") @inline def wallpaper = basis.getWallpaper

  /**
   * Shortcut for `[[https://developer.android.com/reference/android/content/Context.html#setWallpaper(android.graphics.Bitmap) setWallpaper(android.graphics.Bitmap)]]`
   */
  @deprecated("", "") @inline def wallpaper(p: android.graphics.Bitmap) = wallpaper_=(p)

  /**
   * Shortcut for `[[https://developer.android.com/reference/android/content/Context.html#setWallpaper(android.graphics.Bitmap) setWallpaper(android.graphics.Bitmap)]]`
   */
  @deprecated("", "") @inline def wallpaper_=(p: android.graphics.Bitmap) = { basis.setWallpaper(p); basis }

  /**
   * Shortcut for `[[https://developer.android.com/reference/android/content/Context.html#setWallpaper(java.io.InputStream) setWallpaper(java.io.InputStream)]]`
   */
  @deprecated("", "") @inline def wallpaper(p: java.io.InputStream) = wallpaper_=(p)

  /**
   * Shortcut for `[[https://developer.android.com/reference/android/content/Context.html#setWallpaper(java.io.InputStream) setWallpaper(java.io.InputStream)]]`
   */
  @deprecated("", "") @inline def wallpaper_=(p: java.io.InputStream) = { basis.setWallpaper(p); basis }

  /**
   * Shortcut for `[[https://developer.android.com/reference/android/content/Context.html#getWallpaperDesiredMinimumHeight() getWallpaperDesiredMinimumHeight()]]`
   */
  @deprecated("", "") @inline def wallpaperDesiredMinimumHeight = basis.getWallpaperDesiredMinimumHeight

  /**
   * Shortcut for `[[https://developer.android.com/reference/android/content/Context.html#getWallpaperDesiredMinimumWidth() getWallpaperDesiredMinimumWidth()]]`
   */
  @deprecated("", "") @inline def wallpaperDesiredMinimumWidth = basis.getWallpaperDesiredMinimumWidth

  @inline def bindService[T: ClassTag](p1: android.content.ServiceConnection, p2: Int)(implicit context: Context): Boolean = basis.bindService(SIntent[T], p1, p2)

  @inline def removeStickyBroadcast[T: ClassTag](implicit context: Context): Unit = basis.removeStickyBroadcast(SIntent[T])

  @inline def sendBroadcast[T: ClassTag](implicit context: Context): Unit = basis.sendBroadcast(SIntent[T])

  @inline def sendBroadcast[T: ClassTag](p: java.lang.String)(implicit context: Context): Unit = basis.sendBroadcast(SIntent[T], p)

  @inline def sendOrderedBroadcast[T: ClassTag](p: java.lang.String)(implicit context: Context): Unit = basis.sendOrderedBroadcast(SIntent[T], p)

  @inline def sendOrderedBroadcast[T: ClassTag](p1: java.lang.String, p2: android.content.BroadcastReceiver, p3: android.os.Handler, p4: Int, p5: java.lang.String, p6: android.os.Bundle)(implicit context: Context): Unit = basis.sendOrderedBroadcast(SIntent[T], p1, p2, p3, p4, p5, p6)

  @inline def sendStickyBroadcast[T: ClassTag](implicit context: Context): Unit = basis.sendStickyBroadcast(SIntent[T])

  @inline def sendStickyOrderedBroadcast[T: ClassTag](p1: android.content.BroadcastReceiver, p2: android.os.Handler, p3: Int, p4: java.lang.String, p5: android.os.Bundle)(implicit context: Context): Unit = basis.sendStickyOrderedBroadcast(SIntent[T], p1, p2, p3, p4, p5)

  @inline def startActivity[T: ClassTag](implicit context: Context): Unit = basis.startActivity(SIntent[T])

  @inline def startActivity[T: ClassTag](p: android.os.Bundle)(implicit context: Context): Unit = basis.startActivity(SIntent[T], p)

  @inline def startService[T: ClassTag](implicit context: Context): android.content.ComponentName = basis.startService(SIntent[T])

  @inline def stopService[T: ClassTag](implicit context: Context): Boolean = basis.stopService(SIntent[T])
}

/**
 * Enriched trait of the class android.content.Context. To enable Scaloid support for subclasses android.content.Context, extend this trait.
 *
 * Refer the URL below for more details.
 *
 * [[https://github.com/pocorall/scaloid/?134#trait-scontext]]
 *
 */
trait SContext extends Context with TraitContext[SContext] with TagUtil {
  def basis: SContext = this
}

/**
 * Automatically generated enriching class of `[[https://developer.android.com/reference/android/content/ContextWrapper.html android.content.ContextWrapper]]`.
 */
class RichContextWrapper[This <: android.content.ContextWrapper](val basis: This) extends TraitContextWrapper[This]

/**
 * Automatically generated helper trait of `[[https://developer.android.com/reference/android/content/ContextWrapper.html android.content.ContextWrapper]]`. This contains several property accessors.
 */
trait TraitContextWrapper[This <: android.content.ContextWrapper] extends TraitContext[This] {

  /**
   * Shortcut for `[[https://developer.android.com/reference/android/content/ContextWrapper.html#getBaseContext() getBaseContext()]]`
   */
  @inline def baseContext = basis.getBaseContext

}

/**
 * Automatically generated concrete helper class of `[[https://developer.android.com/reference/android/content/ContextWrapper.html android.content.ContextWrapper]]`.
 */
class SContextWrapper()(implicit base: android.content.Context)
  extends android.content.ContextWrapper(base) with TraitContextWrapper[SContextWrapper] {

  def basis = this

}

object SContextWrapper {
  def apply()(implicit base: android.content.Context): SContextWrapper = {
    val v = new SContextWrapper

    v
  }

}

/**
 * When you register BroadcastReceiver with Context.registerReceiver() you have to unregister it to prevent memory leak.
 * Trait UnregisterReceiver handles these chores for you.
 * All you need to do is append the trait to your class.
 *
 * {{{
 * class MyService extends SService with UnregisterReceiver {
 * def func() {
 * // ...
 * registerReceiver(receiver, intentFilter)
 * // Done! automatically unregistered at UnregisterReceiverService.onDestroy()
 * }
 * }
 * }}}
 * See also: [[https://github.com/pocorall/scaloid/wiki/Basics#trait-unregisterreceiver]]
 */
@deprecated("Use ContentHelper.registerReceiver instead", "4.0")
trait UnregisterReceiver extends ContextWrapper with Destroyable {
  /**
   * Internal implementation for (un)registering the receiver. You do not need to call this method.
   */
  override def registerReceiver(receiver: BroadcastReceiver, filter: IntentFilter): android.content.Intent = {
    onDestroy {
      Log.i("ScalaUtils", "Unregister BroadcastReceiver: " + receiver)
      try {
        unregisterReceiver(receiver)
      } catch {
        // Suppress "Receiver not registered" exception
        // Refer to http://stackoverflow.com/questions/2682043/how-to-check-if-receiver-is-registered-in-android
        case e: IllegalArgumentException => e.printStackTrace()
      }
    }

    super.registerReceiver(receiver, filter)
  }
}

/**
 * Provides shortcuts for intent creation.
 *
 * {{{
 *   SIntent[MyActivity]
 * }}}
 *
 */
object SIntent {
  @inline def apply[T](implicit context: Context, mt: ClassTag[T]) = new Intent(context, mt.runtimeClass)

  @inline def apply[T](action: String)(implicit context: Context, mt: ClassTag[T]): Intent = SIntent[T].setAction(action)
}

class RichIntent(val intent: Intent) {
  @inline def start[T <: Activity](implicit context: Context, mt: ClassTag[T]) = {
    val clazz = mt.runtimeClass
    intent.setClass(context, clazz)
    clazz match {
      case c if classOf[Activity].isAssignableFrom(c) =>
        context.startActivity(intent)
      case c if classOf[android.app.Service].isAssignableFrom(c) =>
        context.startService(intent)
    }
  }

  def put(values: Any*): Intent = macro org.scaloid.util.MacroImpl.put_impl
}

/**
 * An in-process service connector that can bound [[LocalService]]. This yields far more concise code than that uses plain-old Android API.
 *
 * Please refer to the URL below for more details.
 *
 * [[http://blog.scaloid.org/2013/03/introducing-localservice.html]]
 */
class LocalServiceConnection[S <: LocalService](bindFlag: Int = Context.BIND_AUTO_CREATE)(implicit ctx: Context, reg: Registerable, mf: ClassTag[S]) extends ServiceConnection {
  var service: Option[S] = None
  var componentName: ComponentName = _
  var binder: IBinder = _
  var onConnected = new EventSource1[S, Unit]
  var onDisconnected = new EventSource1[S, Unit]

  /**
   * Execute given function with the connected service. If the service is not connected yet, the function
   * is enqueued and be called when the service is connected.
   * For example:
   * val service = new LocalServiceConnection[MyService]
   * //...
   * service(_.doSomeJob())
   */
  def apply[T](f: S => Unit): Unit = service.fold(onConnected(f))(f)

  /**
   * Execute given function with the connected service. If the service is not connected yet,
   * this returns ifEmpty value
   * for example:
   * val service = new LocalServiceConnection[MyService]
   * //...
   * val foo = service(_.foo, defaultVal)
   */
  def apply[T](f: S => T, ifEmpty: => T): T = service.fold(ifEmpty)(f)

  /**
   * Execute given function with the connected service. If the service is not connected yet,
   * this does nothing
   * for example:
   * val service = new LocalServiceConnection[MyService]
   * //...
   * val foo = service.ifAvailable(_.foo)
   */
  def ifAvailable[T](f: S => T): Unit = if (service.nonEmpty) f(service.get)

  /**
   * for example:
   * val service = new LocalServiceConnection[MyService]
   * //...
   * val result = service(_.foo > 3, "3 < " + _.foo, "fail")
   */
  def apply[T](test: S => Boolean, ifTrue: S => T, ifFalse: => T) = if (service.nonEmpty && test(service.get)) ifTrue(service.get) else ifFalse

  /**
   * Execute given function and returns value if the service is connected.
   * If the service is not connected yet, this does nothing and returns an empty option
   * for example:
   * val service = new LocalServiceConnection[MyService]
   * //...
   * val fooOption = service.maybe(_.foo)
   */
  def maybe[T](f: S => T): Option[T] = service.map(f)

  /**
   * Internal implementation for handling the service connection. You do not need to call this method.
   */
  def onServiceConnected(p1: ComponentName, b: IBinder): Unit = {
    val svc = b.asInstanceOf[LocalService#ScaloidServiceBinder].service.asInstanceOf[S]
    service = Option(svc)
    componentName = p1
    binder = b
    onConnected.run(svc)
    onConnected.clear()
  }

  /**
   * Internal implementation for handling the service connection. You do not need to call this method.
   */
  def onServiceDisconnected(p1: ComponentName): Unit = {
    service.foreach(onDisconnected.run)
    onDisconnected.clear()
    service = None
  }

  /**
   * Returns true if the service is currently connected.
   */
  def connected: Boolean = service.isDefined

  /**
   * Creating an intent for ctx.bindService(). Extend this when you need to to something other.
   */
  def createIntent(): Intent = SIntent[S]

  reg.onRegister {
    ctx.bindService(createIntent(), this, bindFlag)
  }

  reg.onUnregister {
    if (connected) {
      service = None // prevents apply(...) methods access this after unbound
      ctx.unbindService(this)
      onConnected.clear() // not to be called at the next binding
    }
  }
}
